import Foundation

// Type interface

let constString = "This is a string constant"

let pi = 3.14159

var primeNumber = 691
let name = "My name"

// Type annotation
let pi2: Double = 3.14159
let piAndPhi: (Double, Double) = (3.14159, 1.618)

func ourFunction(a: Int) {
    // Implementation of this function
    // ...
}

// Type aliases
typealias UnsignedInteger = UInt32

// Tuples
// http400Error is of type (Int, String)
let http400Error = (400, "Bad request")

// Decompose a tuple's content
let (requestStatusCode, requestStatusMessage) = http400Error

// Subscripts
struct TimesTable {
    let multipiler: Int
    subscript(index: Int) -> Int {
        return multipiler * index
    }
}

let fiveTimesTable = TimesTable(multipiler: 5)
// prints "six times five is 30"
print("six times five is \(fiveTimesTable[6])")

// String literal
let aVegetable = "Arugula"

// Initializing an Empty String

var anEmptyString = ""

var anotherEmptyString = String()

if anEmptyString.isEmpty {
    print("String is Empty")
}

// Append String and Characters
let string1 = "Hello"
let string2 = "Mr"
var welcome = string1 + string2

var instruction = "Follow us please"
instruction += string2

let exclamationMark: Character = "!"
welcome.append(exclamationMark)

// Protocol works like interface
protocol HttpProtocol {
    func didReceiveResults(result: Any)
}

struct WebServiceManager {
    var delegate: HttpProtocol?
    let data: Data

    func test() {
        do {
            // I don't know JSON stuff in which library or dependency
            /* let jsonResult = try JSONSerialzation.jsonObject(with: self.data, options: JSONSerialzation.ReadingOptions.mutableContainers)     */
            /* let interface run function */
            /* self.delegate?.didReceiveResults(jsonResult) */
        } catch let error as NSError {
            print("json error " + error.localizedDescription)
        }
    }
}

// Optional as a Type
var optionalString: String? = "A String damn"
optionalString = nil

optionalString = "Still back"
print(optionalString)

let nilName: String? = nil
if let familyName = nilName {
    let greetingfamilyName = "Hello, Mr. \(familyName)"
} else {

}

// Optional chain
class Residence {
    var numberOfRooms = 1
}

class Person {
    var residence: Residence?
}

let jeanMarc = Person()
if let roomCount = jeanMarc.residence?.numberOfRooms {
    // use roomCount below code
}

// Nested Type
struct BlackjackCard {
    enum Suit: String {
        case spades = "Spades",
        hearts = "Hearts",
        diamonds = "Diamonds",
        clubs = "Clubs"
    }

    enum Rank: Int {
        case two = 2, three, four
        case jack, queen, king, ace

        struct Values {
            let first: Int, second: Int?    
        }

        var values: Values {
            switch self {
            case .ace:
                return Values(first: 1, second: 11)
            case .jack, .queen, .king:
                return Values(first: 10, second: nil)
            default:
                return Values(first: self.rawValue, second: nil)
            }
        }
    }

    let rank: Rank, suit: Suit
}

// Modern Syntax with Swift6
/* Objc code
// VerboseClass.h
@interface VerboseClass: NSObject
@property (nonatomic, strong)  NSArray *ourArray;
-(void)aMethod: (NSArray *)anArray;
@end

// TestVerbose.Class.m Implements VerboseClass.h file
#import "VerboseClass.h"

@interface TestVerboseClass: NSObject
@end

// This is kind of shit thing in objective-c
@implementation TestVerboseClass
-(void)aMethod {
    VerboseClass *ourObjClass = [[VerboseClass alloc]init];
    [ourObjClass aMethod: @[@"One", @"Two", @"Three"]];
}
@end

*/

class ASwiftClass {
    // by default, this is public
    var ourArray: [String] = []

    func aMethod(anArray: [String]) {
        self.ourArray = anArray
    }
}

let aSwiftClassInstance = ASwiftClass()
aSwiftClassInstance.aMethod(anArray: ["One", "Two", "Three"])
print(aSwiftClassInstance.ourArray)

// Static Method

class AClass {
    class func someTypeMethod() {
    }
}

AClass.someTypeMethod()


// Map Filter Reduce as Functional Programing
let numbers = [10, 30, 91, 50, 100, 93, 74]
var formatedNumbers: [String] = []

for num in numbers {
    let formatedNum = "\(num)"
    formatedNumbers.append(formatedNum)
}

let mappedNumbers = numbers.map {
    // This syntax is damn
    // View this is one element in the array
    "\($0)$"
}

print(mappedNumbers)
// Filter
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers)
// Reduce as Summary
// put the left hand side and right hand side
let total = numbers.reduce(0) { $0 + $1 }
print(total)

// Lazy Evaluation when you use it, it evals, if not use it, not going to evals
let oneToFour  = [1, 2, 3, 4]
let firstNumber = oneToFour.lazy.map({
    // one element * 33
    $0 * 33
}).first!
print(firstNumber)

// Generic Type
// inout will effect the input paramter to out scope
func swapTwoElement<T>(a: inout T, b: inout T) {
    let tmp = a
    a = b
    b = tmp
}

// Extension + Protocol
protocol ExampleProtocol {
    var simpleDescription: String {get}
    mutating func adjust()
}

class SimpleClass : ExampleProtocol {
    var simpleDescription: String = "A very simple class example"

    // if you are in class, no need to use mutating keyword
    // else in struct, require mutating keyword
    func adjust() {
        simpleDescription += "Now 100% adjuested ... "
    }
}

// ext a Type
extension Double {
    var mm: Double { return self / 1_000.0 }
}

let threeInch = 76.2.mm



